/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.admin.service.impl;

import vip.dexian.admin.service.FileService;
import vip.dexian.common.utils.FreeMarkerUtils;
import vip.dexian.common.utils.NumberUtils;
import vip.dexian.core.config.oss.upload.UploadProperties;
import vip.dexian.core.enums.FileTypeEnum;
import vip.dexian.core.exception.ServiceValidationException;
import vip.dexian.thirdparty.manager.OssManager;
import jakarta.annotation.Resource;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 文件处理
 *
 * @author 挺好的 2023年06月01日 11:56
 */
@Service ("fileServiceImpl")
public class FileServiceImpl implements FileService {

    /**
     * 文件上传配置
     */
    @Resource (name = "uploadProperties")
    private UploadProperties uploadProperties;

    /**
     * oss manager
     */
    @Resource (name = "ossManagerImpl")
    private OssManager ossManager;


    /**
     * 校验上传的文件是否合法
     *
     * @param fileType
     *         文件类型
     * @param multipartFile
     *         {@link  org.springframework.web.multipart.MultipartFile}
     */
    @Override
    public void valid (FileTypeEnum fileType, MultipartFile multipartFile) {
        Assert.notNull(fileType, "file type is required");
        Assert.notNull(multipartFile, " multipart file is required");
        Assert.state(!multipartFile.isEmpty(), "multipart file must be not empty");


        Integer uploadMaxSize = this.getUploadMaxSize(fileType);

        // 文件超过限制大小
        if (NumberUtils.isPositive(uploadMaxSize) && multipartFile.getSize() > uploadMaxSize * 1024L * 1024L) {
            throw new ServiceValidationException("00005", uploadMaxSize);
        }

        String[] uploadExtensions = this.getUploadExtensions(fileType);

        // 文件后缀是否允许
        if (ArrayUtils.isNotEmpty(uploadExtensions)) {
            boolean isExtension = FilenameUtils.isExtension(multipartFile.getOriginalFilename(), uploadExtensions);

            if (!isExtension) {
                throw new ServiceValidationException("00006", uploadExtensions);
            }
        } else {
            throw new ServiceValidationException("00006");
        }
    }

    /**
     * 上传文件
     *
     * @param fileType
     *         文件类型
     * @param multipartFile
     *         {@link  org.springframework.web.multipart.MultipartFile}
     * @param async
     *         是否异步上传
     *
     * @return 文件上传成功后的路径
     */
    @Override
    public String upload (FileTypeEnum fileType, MultipartFile multipartFile, boolean async) {

        Assert.notNull(fileType, "file type is required");
        Assert.notNull(multipartFile, " multipart file is required");
        Assert.state(!multipartFile.isEmpty(), "multipart file must be not empty");

        String uploadPath = this.getUploadPath(fileType);

        try {
            Map <String, Object> model = new HashMap <>();
            model.put("uuid", UUID.randomUUID().toString());
            String path = this.uploadProperties.getUploadDirectory() + FreeMarkerUtils.process(uploadPath, model);
            String destPath = path + UUID.randomUUID() + "." + FilenameUtils.getExtension(
                    multipartFile.getOriginalFilename());

            File tempFile = new File(FileUtils.getTempDirectory(), UUID.randomUUID() + ".tmp");
            multipartFile.transferTo(tempFile);

            this.ossManager.upload(destPath, tempFile, multipartFile.getContentType());

            return destPath;

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 使用异步上传
     *
     * @param fileType
     *         文件类型
     * @param multipartFile
     *         {@link  org.springframework.web.multipart.MultipartFile}
     *
     * @return 文件上传成功后的路径
     */
    @Override
    public String upload (FileTypeEnum fileType, MultipartFile multipartFile) {
        Assert.notNull(fileType, "file type is required");
        Assert.notNull(multipartFile, " multipart file is required");
        Assert.state(!multipartFile.isEmpty(), "multipart file must be not empty");

        return this.upload(fileType, multipartFile, true);
    }

    /**
     * 获取上传的路径
     *
     * @param fileType
     *         文件类型
     *
     * @return 上传的路径
     */
    private String getUploadPath (FileTypeEnum fileType) {
        return switch (fileType) {
            case MEDIA -> this.uploadProperties.getMediaPath();
            case FILE -> this.uploadProperties.getFilePath();
            case IMAGE -> this.uploadProperties.getImagePath();
        };
    }

    /**
     * 获取允许上传的后缀
     *
     * @param fileType
     *         文件类型
     *
     * @return 允许上传的后缀
     */
    private String[] getUploadExtensions (FileTypeEnum fileType) {
        return switch (fileType) {
            case MEDIA -> this.uploadProperties.getMediaExtensions();
            case FILE -> this.uploadProperties.getFileExtensions();
            case IMAGE -> this.uploadProperties.getImageExtensions();
        };
    }

    /**
     * 获取文件大小限制
     *
     * @param fileType
     *         文件类型
     *
     * @return 文件大小限制
     */
    private Integer getUploadMaxSize (FileTypeEnum fileType) {
        return switch (fileType) {
            case MEDIA -> this.uploadProperties.getMediaMaxSize();
            case FILE -> this.uploadProperties.getFileMaxSize();
            case IMAGE -> this.uploadProperties.getImageMaxSize();
        };
    }
}
